<!--created by yuyupeng-->
<!--2023/03/10-->
<!-- 人员选择对话框，所有需要选择人员的地方均可使用此组件-->
<template>
  <div class="list-card-container">
    <a-modal
      :width="width"
      :title="title"
      title-align="start"
      :visible="visOfOrgEmployeeSelectModal"
      :mask-closable="maskClosable"
      :unmount-on-close="unmountOnClose"
      :ok-button-props="okButtonProps"
      :cancel-button-props="cancelButtonProps"
      @ok="submitOrgEmployeeSelectModal"
      @cancel="closeOrgEmployeeSelectModal"
    >
      <a-tabs default-active-key="1">
        <a-tab-pane key="1">
          <template #title> <icon-home /> 从组织架构选择 </template>
          <splitpanes class="default-theme">
            <pane min-size="20" size="20">
              <a-card title="组织机构树" class="general-card">
                <a-scrollbar style="height: calc(80vh - 180px); overflow: auto">
                  <a-skeleton v-if="treeData.length === 0" animation>
                    <!-- 还在加载的时候用骨架屏先占个位子，好看点 -->
                    <a-skeleton-line :rows="10" />
                  </a-skeleton>
                  <div v-else>
                    <a-input-search
                      v-model="searchTreeKey"
                      style="margin-bottom: 5px"
                      placeholder="请输入组织机构名称"
                      @input="searchTreeData"
                    />
                    <a-tree
                      v-model:selected-keys="selectedTreeKeys"
                      v-model:expanded-keys="expandedTreeKeys"
                      block-node
                      :field-names="{
                        key: 'code',
                        title: 'name',
                        children: 'children',
                      }"
                      :data="treeDataFilter"
                      @select="onSelectTreeNode"
                      @expand="onExpandTreeNode"
                    >
                      <template #icon>
                        <icon-home />
                      </template>
                      <template #title="nodeData">
                        <span v-if="nodeData?.name.indexOf(searchTreeKey) > -1">
                          {{
                            nodeData?.name.substr(
                              0,
                              nodeData?.name.indexOf(searchTreeKey)
                            )
                          }}
                          <span style="color: red">{{ searchTreeKey }}</span>
                          {{
                            nodeData?.name.substr(
                              nodeData?.name.indexOf(searchTreeKey) +
                                searchTreeKey.length
                            )
                          }}
                        </span>
                        <span v-else>{{ nodeData?.name }}</span>
                      </template>
                    </a-tree>
                  </div>
                </a-scrollbar>
                <template #extra>
                  <a-popover content="刷新组织机构">
                    <icon-refresh @click="getTreeData" />
                  </a-popover>
                </template>
              </a-card>
            </pane>
            <pane min-size="40" size="55">
              <a-card title="人员" class="general-card">
                <a-scrollbar style="height: calc(80vh - 180px); overflow: auto">
                  <a-input-search
                    v-model="searchEmployeeKey"
                    style="width: 40%; margin-bottom: 5px"
                    placeholder="请输入姓名或工号"
                    @input="searchEmployeeData"
                  />
                  <a-skeleton v-if="employeeListLoading" animation>
                    <!-- 还在加载的时候用骨架屏先占个位子，好看点 -->
                    <a-skeleton-line :rows="10" />
                  </a-skeleton>
                  <div v-else>
                    <a-list
                      :bordered="false"
                      split
                      size="small"
                      :data="tbData"
                      :pagination-props="paginationProps"
                      @page-change="onPageChange"
                    >
                      <template v-if="!onlyChosenOneEmployee" #header>
                        <a-checkbox
                          :model-value="chosenAllFlag"
                          @change="chosenEmployeesAll"
                          >全选</a-checkbox
                        >
                      </template>
                      <template #item="{ item }">
                        <a-list-item>
                          <template #actions>
                            <!-- TODO 这个添加到常用联系人后续还需要双向绑定 -->
                            <!--                          <a-popover
                              v-if="item.index % 2 === 0"
                              content="添加到常用联系人"
                            >
                              <a-rate allow-clear :count="1" />
                            </a-popover>-->
                            <a-rate readonly :default-value="1" :count="1" />
                          </template>
                          <a-list-item-meta
                            :title="item.username"
                            @click="changeChosenFlag(!item.chosenFlag, item)"
                          >
                            <template #avatar>
                              <a-space>
                                <a-radio
                                  v-if="onlyChosenOneEmployee"
                                  :model-value="item.chosenFlag"
                                ></a-radio>
                                <a-checkbox
                                  v-else
                                  :model-value="item.chosenFlag"
                                ></a-checkbox>
                                <a-avatar
                                  :size="38"
                                  shape="square"
                                  :style="{ backgroundColor: '#168CFF' }"
                                >
                                  <img
                                    v-if="item.avatar"
                                    alt="avatar"
                                    :src="item.avatar"
                                  />
                                  <span v-else>{{ item.username }}</span>
                                </a-avatar>
                              </a-space>
                            </template>
                            <template #description>
                              {{ item.userCode }}
                              <a-divider direction="vertical" />
                              {{ item.deptName }}
                            </template>
                          </a-list-item-meta>
                        </a-list-item>
                      </template>
                    </a-list>
                  </div>
                </a-scrollbar>
                <template #extra>
                  <a-popover content="刷新人员列表">
                    <icon-refresh @click="refreshEmployeeList" />
                  </a-popover>
                </template>
              </a-card>
            </pane>
            <pane min-size="25" size="25">
              <a-card title="选中的人员" class="general-card">
                <template #title>
                  选中的人员({{ chosenEmployees.length }})
                </template>
                <template #extra>
                  <a-popover content="清空">
                    <icon-delete @click="removeChosenEmployeeAll" />
                  </a-popover>
                </template>
                <a-scrollbar style="height: calc(80vh - 180px); overflow: auto">
                  <a-row :gutter="[5, 5]" style="margin-top: 5px">
                    <a-col
                      v-for="item in chosenEmployees"
                      :key="item.id"
                      :span="6"
                    >
                      <a-popover>
                        <template #content>
                          {{ item.userCode }}<br />
                          {{ item.username }}<br />
                          {{ item.deptName }}
                        </template>
                        <a-space direction="vertical" size="mini">
                          <a-badge>
                            <template #content>
                              <icon-close-circle
                                style="color: red"
                                @click="removeChosenEmployee(item)"
                              />
                            </template>
                            <a-avatar
                              shape="square"
                              :style="{ backgroundColor: '#168CFF' }"
                            >
                              <img
                                v-if="item.avatar"
                                alt="avatar"
                                :src="item.avatar"
                              />
                              <span v-else>{{ item.username }}</span>
                            </a-avatar>
                          </a-badge>
                          <span style="font-size: 12px">{{
                            item.username
                          }}</span>
                        </a-space>
                      </a-popover>
                    </a-col>
                  </a-row>
                </a-scrollbar>
              </a-card>
            </pane>
          </splitpanes>
        </a-tab-pane>
        <a-tab-pane key="2">
          <template #title> <icon-user-group /> 常用人员 </template>
          <splitpanes class="default-theme">
            <pane min-size="10" size="20">
              <a-card title="常用人员分组" class="general-card">
                <a-scrollbar style="height: calc(80vh - 180px); overflow: auto">
                  <a-skeleton animation>
                    <!-- 用骨架屏先占个位子，好看点 -->
                    <a-skeleton-line :rows="10" />
                  </a-skeleton>
                </a-scrollbar>
              </a-card>
            </pane>
            <pane min-size="50" size="80">
              <a-card title="人员" class="general-card"> </a-card>
            </pane>
          </splitpanes>
        </a-tab-pane>
      </a-tabs>
    </a-modal>
  </div>
</template>

<!--新script-->
<script lang="ts" setup>
  import { Notification } from '@arco-design/web-vue';
  import { queryDeptRecordTreeList } from '@/api/system/dept';
  import { ref, onMounted, defineExpose, toRefs, reactive, watch } from 'vue';
  import { Splitpanes, Pane } from 'splitpanes';
  import 'splitpanes/dist/splitpanes.css';
  import { Pagination } from '@/types/global';
  import { UserRecord } from '@/api/system/model/userModel';
  import { queryUserRecordListAll } from '@/api/system/user';

  const props = defineProps({
    width: { type: String, default: '1200px' }, // 对话框宽度
    title: { type: String, default: '用户对话框' }, // 对话标题
    okText: { type: String, default: '提交' }, // 按钮文字
    onlyChosenOneEmployee: { type: Boolean, default: false }, // 是否只选择一个人员，如果true，则出现单选框，如果false，则出现复选框
    maskClosable: { type: Boolean, default: false }, // 控制是否允许点击蒙层关闭弹窗
    // cancelText: { type: String, default: '取消' }, // 按钮文字
    submitWithClose: { type: Boolean, default: true }, // 用于控制submit回调时，是否进行close操作
    unmountOnClose: { type: Boolean, default: true }, // 关闭时销毁 Modal 里的子元素
    // okLoading: { type: Boolean, default: false }, // 确定按钮 loading
    okButtonProps: { type: Object, default: () => {} },
    cancelButtonProps: { type: Object, default: () => {} },
    defaultSelectedUserCodeList: { type: Array, default: () => [] }, // 默认选中的人员工号列表，例如['199439']
  });
  const emits = defineEmits(['open', 'close', 'submit']);
  /**
   * data
   */
  const {
    width, // 对话框宽度
    title, // 对话标题
    onlyChosenOneEmployee, // 是否只选择一个人员，如果true，则出现单选框，如果false，则出现复选框
    // okText, // 按钮文字
    maskClosable, // 控制是否允许点击蒙层关闭弹窗
    // cancelText, // 按钮文字
    // submitWithClose, // 用于控制submit回调时，是否进行close操作
    unmountOnClose, // 关闭时销毁 Modal 里的子元素
    // okLoading, // 确定按钮 loading
    okButtonProps,
    cancelButtonProps,
    defaultSelectedUserCodeList, // 默认选中的人员工号列表，例如['199439']
  } = toRefs(props); // 根据props创建多个 ref 对象
  const treeData = ref<any[]>([]); // 树
  const treeDataFilter = ref<any[]>([]); // 用于搜索的过滤树
  const filterKeySet = new Set(); // 定义一个集合，用于记录过滤keys
  const searchTreeKey = ref<string>(); // 树顶上的搜索框关键字
  const dataList = ref<any[]>([]); // treeData的所有key:title
  const parentKeys = ref<string[]>([]); // 用于存储点击的树节点的所有父节点的key
  const visOfOrgEmployeeSelectModal = ref<boolean>(false); // 控制用户对话框是否弹出
  const treeDataLoading = ref<boolean>(false); // 指示树形组件是否正在加载中
  const expandedTreeKeys = ref<string[]>([]); // 已展开的树节点keys
  const selectedTreeKeys = ref<string[]>([]); // 已选中的树节点keys
  const currentSelectDeptTreeNode = ref<any>(); // 记录当前选中的左侧组织机构树节点
  const searchEmployeeKey = ref<string>(); // 人员列表顶上的搜索框关键字
  const chosenAllFlag = ref<boolean>(false); // 是否全选人员列表
  const chosenEmployees = ref<any[]>([]); // 选中的人员列表
  const tbData = ref<UserRecord[]>([]); // 人员列表数据源
  const employeeListLoading = ref<boolean>(false); // 指示人员列表是否正在加载中
  const otherParamsList = ref<any[]>([]); // 其他附加参数
  // 人员列表分页，默认第1页，每页3条，条数不可更改
  const basePagination: Pagination = {
    current: 1,
    pageSize: 4,
    showPageSize: false,
    showTotal: true,
  };
  const paginationProps = reactive({
    ...basePagination,
  });
  /**
   * watch
   */
  watch(
    () => parentKeys,
    (val) => {
      if (val) {
        val.value.forEach((element: any) => {
          filterKeySet.add(element);
        });
        // 进行过滤操作
        treeDataFilter.value = treeData.value.filter((item) =>
          filterKeySet.has(item.code)
        );
        expandedTreeKeys.value = [...filterKeySet] as string[];
      }
    },
    { deep: true, immediate: true }
  );
  /**
   * func
   */
  /**
   * @description 获取部门的树形结构信息
   */
  const getTreeData = async () => {
    treeDataLoading.value = true;
    treeData.value = [];
    treeDataFilter.value = [];
    try {
      const { data } = await queryDeptRecordTreeList({});
      treeData.value = data;
      treeDataFilter.value = data;
      dataList.value = [];
      generateList(treeData.value);
    } catch (error) {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      Notification.error({
        content: `出现异常！${error}`,
        closable: true,
        duration: 20000,
      });
    } finally {
      treeDataLoading.value = false;
    }
  };
  /**
   * @description 构建treeData的所有 { key: title } 数组
   * @param data 树节点数据
   */
  const generateList = (data: any) => {
    // 创建dataList 递归调用
    data.forEach((item: any) => {
      const node = item;
      const key = node.code;
      dataList.value.push({ key, title: node.name });
      if (node.children) {
        generateList(node.children);
      }
    });
  };
  /**
   * @description 根据子节点的key获取父节点的key
   * @param key key
   * @param tree 树节点
   */
  const getParentKey = (key: string, tree: any) => {
    let parentKey;
    tree.forEach((elem: any) => {
      const node = elem;
      if (node.children) {
        if (node.children.some((item: any) => item.code === key)) {
          parentKey = node.code;
        } else if (getParentKey(key, node.children)) {
          parentKey = getParentKey(key, node.children);
        }
      }
    });
    return parentKey;
  };
  /**
   * @description 获取完整地父节点的key数组 递归调用
   * @param key
   */
  const getWholeParentKeys = (key: string) => {
    const temp = getParentKey(key, treeData.value);
    if (temp !== undefined) {
      parentKeys.value.push(temp);
      getWholeParentKeys(temp);
    }
  };
  /**
   * @description 树形结构上面的搜索框发生改变会触发此函数
   * @param value
   */
  const searchTreeData = (val: any) => {
    const value = val.replaceAll(' ', '');
    filterKeySet.clear();
    // 进行搜索操作
    const expandedKeysTemp = [] as any[];
    dataList.value.forEach((item) => {
      if (item.title && item.title.indexOf(value) > -1) {
        expandedKeysTemp.push(item.key);
      }
    });
    parentKeys.value = [];
    // 必须要有下面这个forEach，否则只能查到2层数据，因为超过了2层后匹配不到更上层的父id，进而导致搜索不出结果
    expandedKeysTemp.forEach((item: string) => {
      filterKeySet.add(item);
      getWholeParentKeys(item); // 把原始的expandedKeysTemp中的每个key对应的完整父key都放进parentKeys保存
    });
    expandedTreeKeys.value =
      value.trim().length === 0 ? [] : ([...filterKeySet] as string[]);
    searchTreeKey.value = value;
    // 进行过滤操作
    treeDataFilter.value = treeData.value.filter((item) =>
      filterKeySet.has(item.code)
    );
  };
  /**
   * 点击树节点触发
   * @param newSelectedKeys
   * @param event
   */
  const onSelectTreeNode = (newSelectedKeys: any, event: any) => {
    // console.log('select: ', newSelectedKeys, event);
    selectedTreeKeys.value = newSelectedKeys;
    currentSelectDeptTreeNode.value = event.node;
    if (event.node.children) {
      let flag = false;
      expandedTreeKeys.value.forEach((item: string) => {
        if (item === newSelectedKeys[0]) {
          flag = true;
        }
      });
      if (!flag) {
        expandedTreeKeys.value.push(newSelectedKeys[0]);
      }
    }
    getTbData({ deptCode: newSelectedKeys[0] });
  };
  /**
   * 展开树节点触发
   * @param newExpandedKeys
   * @param event
   */
  const onExpandTreeNode = (newExpandedKeys: any, event: any) => {
    console.log('expand: ', newExpandedKeys, event);
  };
  /**
   * @description 用户对话框提交触发
   */
  const submitOrgEmployeeSelectModal = () => {
    emits('submit', chosenEmployees.value, otherParamsList.value); // submit回调返回选中的人员信息和其他参数
    visOfOrgEmployeeSelectModal.value = false;
  };
  /**
   * @description 用户对话框打开触发
   * @param otherParams 其他参数
   */
  const openOrgEmployeeSelectModal = async (otherParams = []) => {
    visOfOrgEmployeeSelectModal.value = true;
    otherParamsList.value = otherParams;
    await getTbData();
    // 每次打开人员对话框时都做一系列的重置操作
    resetOrgEmployeeSelectModal();
    if (defaultSelectedUserCodeList.value.length !== 0) {
      // 如果有默认选中的需求
      if (onlyChosenOneEmployee?.value) {
        // 单选模式，只选中第一个
        const chosenEmployee = tbData.value.filter(
          (el) => el.userCode === defaultSelectedUserCodeList.value[0]
        )[0] as any;
        chosenEmployee.chosenFlag = true;
        chosenEmployees.value.push(chosenEmployee);
      } else {
        // 多选模式
        defaultSelectedUserCodeList.value.forEach((item) => {
          const chosenEmployee = tbData.value.filter(
            (el) => el.userCode === item
          )[0] as any;
          chosenEmployee.chosenFlag = true;
          chosenEmployees.value.push(chosenEmployee);
        });
      }
    }
    emits('open');
  };
  /**
   * @description 用户对话框关闭触发
   */
  const closeOrgEmployeeSelectModal = () => {
    visOfOrgEmployeeSelectModal.value = false;
    emits('close');
  };
  /**
   * @method
   * @description 页码发生变化触发
   */
  const onPageChange = (current: number) => {
    paginationProps.current = current;
  };
  /**
   * @description 人员列表单选框变化触发
   * @param value 始终为true
   * @param item 点击的人员列表的item
   */
  const onlyChosenThisEmployee = (value: boolean, item: any) => {
    tbData.value.forEach((elem: any) => {
      if (elem.id === item.id) {
        elem.chosenFlag = true;
      } else {
        elem.chosenFlag = false;
      }
    });
    chosenEmployees.value = [];
    chosenEmployees.value.push(item);
  };
  /**
   * @description 人员列表勾选框变化触发
   * @param value 是否选中 true false
   * @param item 点击的人员列表的item
   */
  const changeChosenFlag = (value: boolean, item: any) => {
    if (onlyChosenOneEmployee.value) {
      // 如果是单选模式
      onlyChosenThisEmployee(true, item);
    } else {
      // 如果是多选模式
      item.chosenFlag = value;
      if (value) {
        chosenEmployees.value.push(item);
      } else {
        chosenEmployees.value = chosenEmployees.value.filter(
          (elem: any) => elem.id !== item.id
        );
      }
    }
  };
  /**
   * @description 移除已选中的人员
   * @param item 点击的人员列表的item
   */
  const removeChosenEmployee = (item: any) => {
    item.chosenFlag = false;
    chosenEmployees.value = chosenEmployees.value.filter(
      (elem: any) => elem.id !== item.id
    );
  };
  /**
   * @description 清空所有已选中的人员
   */
  const removeChosenEmployeeAll = () => {
    chosenAllFlag.value = false;
    tbData.value.forEach((item: any) => {
      item.chosenFlag = false;
    });
    chosenEmployees.value = [];
  };
  /**
   * 全选复选框触发
   * @param value true false
   */
  const chosenEmployeesAll = (value: boolean) => {
    chosenAllFlag.value = value;
    if (value) {
      tbData.value.forEach((item: any) => {
        item.chosenFlag = true;
        if (
          chosenEmployees.value.filter((elem: any) => elem.id === item.id)
            .length === 0
        ) {
          chosenEmployees.value.push(item);
        }
      });
    } else {
      tbData.value.forEach((item: any) => {
        removeChosenEmployee(item);
      });
    }
  };
  /**
   * @description 一系列的重置操作
   */
  const resetOrgEmployeeSelectModal = () => {
    // getTreeData(); // 暂时考虑的是不要每次打开对话框都查一遍数据源
    searchTreeKey.value = undefined;
    selectedTreeKeys.value = [];
    expandedTreeKeys.value = [];
    currentSelectDeptTreeNode.value = undefined; // 记录的当前选中的左侧组织机构树节点也要重置
    removeChosenEmployeeAll();
  };
  /**
   * @description 根据deptCode给tbData设置部门名称
   */
  const setDeptName = () => {
    tbData.value.forEach((elem: any) => {
      elem.deptName = dataList.value.filter(
        (el) => el.key === elem.deptCode
      )[0].title;
    });
  };
  /**
   * @method
   * @description 获取人员列表数据
   */
  const getTbData = async (reqdata: any = {}) => {
    try {
      employeeListLoading.value = true;
      let { data } = await queryUserRecordListAll();
      if (reqdata.searchEmployeeKey) {
        // 对用户名和工号进行模糊匹配
        data = data.filter(
          (el) =>
            el.username.indexOf(reqdata.searchEmployeeKey) !== -1 ||
            el.userCode.indexOf(reqdata.searchEmployeeKey) !== -1
        );
      }
      if (reqdata.deptCode) {
        // 对部门编号进行精确匹配
        data = data.filter((el) => el.deptCode === reqdata.deptCode);
      }
      data.forEach((item: any) => {
        if (
          chosenEmployees.value.filter((el) => el.id === item.id).length > 0
        ) {
          item.chosenFlag = true; // 每次新查找的tbData的chosenFlag都要与已选中的人员保持一致
        } else {
          item.chosenFlag = false; // 每次新查找的tbData的chosenFlag都要与已选中的人员保持一致
        }
      });
      tbData.value = data;
      paginationProps.current = 1;
      paginationProps.total = Number(data.length); // 用Number函数转一下，否则f12控制台会有警告，很不好看
      setTimeout(() => {
        // 等1200ms后再设置部门名称，以免dataList还没构建完成
        setDeptName();
      }, 1200);
    } catch (error) {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      Notification.error({
        content: `出现异常！${error}`,
        closable: true,
        duration: 20000,
      });
    } finally {
      employeeListLoading.value = false;
    }
  };
  /**
   * @description 用户列表上面的搜索框发生改变会触发此函数
   * @param value
   */
  const searchEmployeeData = (val: any) => {
    const value = val.replaceAll(' ', '');
    searchEmployeeKey.value = value;
    getTbData({ searchEmployeeKey: value });
  };
  /**
   * @description 刷新当前人员列表
   */
  const refreshEmployeeList = () => {
    if (currentSelectDeptTreeNode.value) {
      // 按条件刷新选中的组织机构节点对应的人员列表
      getTbData({ deptCode: currentSelectDeptTreeNode.value.code });
    } else {
      getTbData();
    }
  };
  /**
   * onMounted
   */
  onMounted(() => {
    getTreeData();
    getTbData();
  });
  defineExpose({ openOrgEmployeeSelectModal, closeOrgEmployeeSelectModal }); // 在<srcipt setup>的写法中，想要子组件中的方法可以被父组件调用，要通过defineExpose将方法暴露出去
</script>

<style lang="less" scoped>
  .list-card-container {
    padding: 20px;
    // background-color: var(--color-bg-1);
    border-radius: var(--border-radius-default);
  }
</style>

<style lang="less">
  // splitpanes分割组件的样式, 这个是对着f12窗口调整的，一开始直接放在上面的style里面不生效，网上查资料发现f12窗口复制出来的样式要单独放在一个没有scoped的style标签里才行！
  /* stylelint-disable selector-class-pattern */
  .splitpanes.default-theme .splitpanes__splitter {
    background-color: rgb(242 243 245);
  }
  /* stylelint-disable selector-class-pattern */
</style>
